﻿global using System.Text;
global using System.Text.Json;
global using System.Text.RegularExpressions;
using System.Reflection;

namespace ZGDisassembler.Core;

public class Disassembler
{
	public const string Version = "v0.3.1";
	public AppEnv AppEnv { get; set; } = new();
	public string[] GetAllSupportPlatform => allPlatformType.Keys.ToArray();
	private Dictionary<string, Type> allPlatformType { get; set; } = new();
	private Platform platform { get; set; }

	#region 构造函数
	public Disassembler()
	{
		var allType = Assembly.GetExecutingAssembly().GetTypes();
		foreach (var type in allType)
		{
			var t = type.GetCustomAttribute<PlatformAttribute>();
			if (t == null)
				continue;

			allPlatformType.Add(t.Name, type);
		}
	}
	#endregion 构造函数

	#region 初始化平台
	/// <summary>
	/// 初始化平台
	/// </summary>
	/// <param name="config">配置文件</param>
	public void Initialize(string configFilePath = null)
	{
		AppEnv.AppConfig.ReadConfig(configFilePath);
		if (!allPlatformType.ContainsKey(AppEnv.AppConfig.Platform))
			throw new Exception($"不支持平台 {AppEnv.AppConfig.Platform}");

		AppEnv.AllLabels.Clear();
		AppEnv.AllResult.Clear();
		AppEnv.OrgLabels.Clear();

		AppEnv.ReadReplaceLabel(AppEnv.AppConfig.ReplaceLabelConfig);
		platform = (Platform)Activator.CreateInstance(allPlatformType[AppEnv.AppConfig.Platform], AppEnv)!;
		platform.Initialize();
	}
	#endregion 初始化平台

	#region 读取文件
	/// <summary>
	/// 读取文件
	/// </summary>
	/// <param name="filePath"></param>
	/// <returns></returns>
	public void LoadFile(string filePath, uint fileStart, uint? length = null)
	{
		if (!File.Exists(filePath))
			throw new FileNotFoundException($"文件 \"{filePath}\" 不存在");
		
		AppEnv.SetData(File.ReadAllBytes(filePath), fileStart, length);
	}
	#endregion 读取文件

	#region 加载16进制字符串
	/// <summary>
	/// 加载16进制字符串，不会加载配置信息，需要另外加载
	/// </summary>
	/// <param name="hexString">16进制字符串</param>
	/// <returns></returns>
	public void LoadHexString(string hexString)
	{
		hexString = Regex.Replace(hexString, "[ \t\r\n]", "");
		if (!Regex.IsMatch(hexString, "[0-9a-fA-F]+") && hexString.Length % 2 != 0)
			throw new Exception("输入的字符串不满足");

		var result = new List<byte>();
		for (var i = 0; i < hexString.Length; i += 2)
			result.Add(Convert.ToByte(hexString.Substring(i, 2), 16));

		AppEnv.SetData(result.ToArray(), 0);
	}
	#endregion 加载16进制字符串

	#region 解码数据
	/// <summary>
	/// 解码数据
	/// </summary>
	/// <param name="orgAddress"></param>
	public void DecodeDatas(uint orgAddress)
	{
		if (platform == null)
			throw new Exception("平台尚未初始化");

		AppEnv.AllResult.Clear();
		AppEnv.ResultLineNumberMap.Clear();

		AppEnv.OrgStart = orgAddress;
		uint readLength = 0;
		uint dataIndex = AppEnv.BaseAddress;
		while (dataIndex < AppEnv.FileDataLength)
		{
			if (readLength >= AppEnv.FileReadLength)
				break;

			var temp = platform.Decode(orgAddress, dataIndex);
			if (temp == null)
				break;

			if (temp.TotalLength < 1)
				throw new Exception("解析结果长度小于1");

			AppEnv.AllResult.Add(orgAddress, temp);

			orgAddress += temp.TotalLength;
			dataIndex += temp.TotalLength;

			readLength += temp.TotalLength;
		}
		AppEnv.OrgEnd = orgAddress;

		var prefix = "label";
		if (!string.IsNullOrWhiteSpace(AppEnv.AppConfig.LabelName))
			prefix = AppEnv.AppConfig.LabelName;

		AppEnv.AllLabels.Clear();
		var labelIndex = AppEnv.AppConfig.LabelStartIndex;
		foreach (var kvp in AppEnv.AllResult)
		{
			for (var i = 0; i < kvp.Value.Addressings.Length; i++)
			{
				var address = kvp.Value.Addressings[i];
				//if (address < AppEnv.OrgEnd && address >= AppEnv.OrgStart)
				if (AppEnv.AllResult.ContainsKey(address))
				{
					AppEnv.AllLabels.TryAdd(address, $"{prefix}{labelIndex}");
					labelIndex++;
				}
			}
		}
	}
	#endregion 解码数据

	#region 输出结果
	public string OutputResult()
	{
		if (platform == null)
			throw new Exception("平台尚未初始化");
		
		StringBuilder result = new($"\t.ORG {platform.ToHexString(AppEnv.OrgStart)}\n");

		var orgAddress = AppEnv.OrgStart;
		var addressOffset = AppEnv.BaseAddress - AppEnv.OrgStart;
		var isData = false;
		uint lineIndex = 1;
		List<byte> dataTemp = new();

		#region 添加Label
		void AddLabel(uint address)
		{
			if (AppEnv.AppConfig.AbleLabel == false)
				return;

			var label = AppEnv.GetLabel(address);
			if (string.IsNullOrWhiteSpace(label))
				return;

			result.Append($"{label}\n");
			lineIndex++;
		}
		#endregion 添加Label

		#region 拼合数据
		void CombineData()
		{
			if (!isData)
				return;

			uint tempAddress = orgAddress - (uint)dataTemp.Count;
			var start = true;
			isData = false;
			for (uint i = 0; i < dataTemp.Count; i++)
			{
				var label = AppEnv.GetLabel(tempAddress + i);
				if (label != null)
				{
					if (!start)
					{
						result.Append('\n');
						lineIndex++;
					}

					result.Append($"{label}\n\t.HEX");
					lineIndex++;
					start = false;
				}
				else if (start)
				{
					result.Append("\t.HEX");
					start = false;
				}
				result.Append(' ').Append(dataTemp[(int)i].ToString("X2"));
			}
			AddLineAddress(tempAddress);
			result.Append('\n');
			lineIndex++;
			dataTemp.Clear();
		}
		#endregion 拼合数据

		void AddLineAddress(uint orgAddress) => AppEnv.ResultLineNumberMap.TryAdd(lineIndex, (orgAddress, orgAddress + addressOffset));

		List<string> tempList = new();
		while(orgAddress < AppEnv.OrgEnd)
		{
			if (!AppEnv.AllResult.TryGetValue(orgAddress, out var line))
				break;

			switch(line.LineType)
			{
				case DecodeType.Data:
					isData = true;
					for (var i = 0; i < line.Addressings.Length; i++)
						dataTemp.Add((byte)line.Addressings[i]);

					break;
				case DecodeType.Instruction:
					CombineData();
					AddLabel(orgAddress);
					var mode = line.SourceMode;
					result.Append('\t').Append(mode.Instruction);
					if (!string.IsNullOrWhiteSpace(mode.AddressingMode))
					{
						tempList.Clear();
						for (var i = 0; i < line.Addressings.Length; i++)
						{
							var label = AppEnv.GetLabel(line.Addressings[i]);
							if (!string.IsNullOrWhiteSpace(label))
								tempList.Add(label);

							tempList.Add(platform.ToHexString(line.Addressings[i], mode.AddressLength[i]));
						}
						result.Append(' ').AppendFormat(mode.AddressingMode, tempList.ToArray());
					}
					AddLineAddress(orgAddress);
					result.Append('\n');
					lineIndex++;
					break;
			}
			orgAddress += line.TotalLength;
		}

		return result.ToString();
	}
	#endregion 输出结果

}